#include <net-snmp/net-snmp-config.h>
#include <net-snmp/net-snmp-includes.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/file.h>


struct host {                            /* a list of hosts to query */
  const char *name;
  const char *community;
} hosts[] = {
  { "10.1.1.5",	"public" },
  { "10.1.1.6",	"public" },
  { "10.1.1.9",	"public" },
  { "10.1.1.10", "public" },
  { NULL }
};

struct oid {                        /* a list of variables to query for */
  const char *Name;
  oid Oid[MAX_OID_LEN];
  int OidLen;
} oids[] = {
  { ".1.3.6.1.2.1.1.1.0" },  // system.sysDescr.0
  { NULL }
  // { "system.sysDescr.0" },
  // { "interfaces.ifNumber.1" },
  // { "interfaces.ifNumber.0" },
  // { NULL }
};
// -------------------------------void initialize(void):处理struct oid中的Oid[MAC_OID_LEN]与OidLen-------------------------------
void initialize (void)
{
  struct oid *op = oids;    
  init_snmp("asynchapp");         /* initialize library */
  
  while (op->Name) {               /* parse the oids */
    op->OidLen = sizeof(op->Oid)/sizeof(op->Oid[0]);
    if (!read_objid(op->Name, op->Oid, &op->OidLen)) {
      snmp_perror("read_objid");
      exit(1);
    }
    op++;
  }
}
// --------------------------------------------------------------------int print_result()-----------------------------------------------------------------------------------------------
int print_result (int status, struct snmp_session *sp, struct snmp_pdu *pdu)
{
  char buf[1024];
  struct variable_list *vp;
  int ix;
  struct timeval now;
  struct timezone tz;
  struct tm *tm;

  gettimeofday(&now, &tz);
  tm = localtime(&now.tv_sec);
  fprintf(stdout, "%.2d:%.2d:%.2d.%.6d ", tm->tm_hour, tm->tm_min, tm->tm_sec,
          now.tv_usec);
  switch (status) {
  case STAT_SUCCESS:
    vp = pdu->variables;
    if (pdu->errstat == SNMP_ERR_NOERROR) {
      while (vp) {
        snprint_variable(buf, sizeof(buf), vp->name, vp->name_length, vp);
        fprintf(stdout, "%s: %s\n", sp->peername, buf);
	vp = vp->next_variable;
      }
    }
    else {
      for (ix = 1; vp && ix != pdu->errindex; vp = vp->next_variable, ix++)
        ;
      if (vp) snprint_objid(buf, sizeof(buf), vp->name, vp->name_length);
      else strcpy(buf, "(none)");
      fprintf(stdout, "%s: %s: %s\n",
      	sp->peername, buf, snmp_errstring(pdu->errstat));
    }
    return 1;
  case STAT_TIMEOUT:
    fprintf(stdout, "%s: Timeout\n", sp->peername);
    return 0;
  case STAT_ERROR:
    snmp_perror(sp->peername);
    return 0;
  }
  return 0;
}

// ************************************** poll all hosts in parallel */****************************************************
struct session {
  struct snmp_session *sess;		/* SNMP session data */
  struct oid *current_oid;		    /* How far in our poll are we */
} sessions[sizeof(hosts)/sizeof(hosts[0])];
       
int active_hosts;			         /* hosts that we have not completed */
  
int asynch_response(int operation, struct snmp_session *sp, int reqid, struct snmp_pdu *pdu, void *magic)    /* response handler */
{
  struct session *host = (struct session *)magic;
  struct snmp_pdu *req;

  if (operation == NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
    if (print_result(STAT_SUCCESS, host->sess, pdu)) {
      host->current_oid++;			/* send next GET (if any) */
      if (host->current_oid->Name) {
	req = snmp_pdu_create(SNMP_MSG_GET);
	snmp_add_null_var(req, host->current_oid->Oid, host->current_oid->OidLen);
	if (snmp_send(host->sess, req))
	  return 1;
	else {
	  snmp_perror("snmp_send");
	  snmp_free_pdu(req);
	}
      }
    }
  }
  else
    print_result(STAT_TIMEOUT, host->sess, pdu);  
  active_hosts--;                            /* something went wrong (or end of variables) * this host not active any more */
    return 1;
}

// ---------------------------------------------------------------------void asynchronous(void)-------------------------------------------------------------------------------
void asynchronous(void)
{
  struct session *hs;
  struct host *hp;  

   //给每台主机发送oids[]中的第一个对象的请求，for循环发送，尔后每台主机返回响应后，再oids++，再向该主机发送下一个对象
    //请求数据报（肯定是在回调函数中完成的）
 for (hs = sessions, hp = hosts; hp->name; hs++, hp++) {                 /* startup all hosts */
    struct snmp_pdu *req;
    struct snmp_session sess;
    snmp_sess_init(&sess);			         /* initialize session */
    sess.version = SNMP_VERSION_2c;
    sess.peername = strdup(hp->name);
    sess.community = strdup(hp->community);
    sess.community_len = strlen(sess.community);
    sess.callback = asynch_response;		/* default callback */
    sess.callback_magic = hs;
    if (!(hs->sess = snmp_open(&sess))) {
      snmp_perror("snmp_open");
      continue;
    }
    hs->current_oid = oids;
    req = snmp_pdu_create(SNMP_MSG_GET);	/* send the first GET */
    snmp_add_null_var(req, hs->current_oid->Oid, hs->current_oid->OidLen);
    if (snmp_send(hs->sess, req))
      active_hosts++;
    else {
      snmp_perror("snmp_send");
      snmp_free_pdu(req);
    }
  }

  while (active_hosts) {           /* loop while any active hosts */
    int fds = 0, block = 1;
    fd_set fdset;
    struct timeval timeout;

    FD_ZERO(&fdset);
    snmp_select_info(&fds, &fdset, &timeout, &block);
    fds = select(fds, &fdset, NULL, NULL, block ? NULL : &timeout);
    if (fds < 0) {
        perror("select failed");
        exit(1);
    }
    if (fds)
        snmp_read(&fdset);
    else
        snmp_timeout();
  }
  
  for (hp = hosts, hs = sessions; hp->name; hs++, hp++) {   /* cleanup */
    if (hs->sess) snmp_close(hs->sess);
  }
}

// 单实例：检查并创建PID文件，确保单实例运行
int check_single_instance() {
    const char* pid_file = "example.pid";
    
    // 打开PID文件（如果不存在则创建）
    int fd = open(pid_file, O_RDWR | O_CREAT, 0644);
    if (fd == -1) {
        perror("无法打开PID文件");
        return -1;
    }
    
    // 尝试获取文件锁（非阻塞模式）
    if (flock(fd, LOCK_EX | LOCK_NB) == -1) {
        close(fd);
        fprintf(stderr, "程序已在运行中（PID文件已锁定）\n");
        return -1;
    }
    
    // 写入当前进程PID
    char pid_str[16];
    snprintf(pid_str, sizeof(pid_str), "%d\n", getpid());
    ftruncate(fd, 0);  // 清空文件
    write(fd, pid_str, strlen(pid_str));
    
    // 注意：这里故意不关闭文件描述符，保持锁有效
    return fd;
}


// /**************************************/
int main (int argc, char **argv)
{
  int pid_fd = check_single_instance();
    if (pid_fd == -1) {
	  exit(1);
  }
  // 守护进程   
  daemon(1, 0);

  initialize();
  printf("---------- asynchronous -----------\n");
  asynchronous();
  
  close(pid_fd);
  unlink("example.pid");
  return 0;
}
